/*
 * Copyright 2009-2015 by WWW.KNET.CN  All rights reserved.
 */

package cn.knet.sp.ncrm.common.web;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;

import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import cn.knet.sp.ncrm.common.aspect.ExportLockAspect;


/**
 * 转发导出表请求,发生错误则servelt中写入一段js alert并跳转回去
 *
 * @author zhangbin
 * @version 2015 /05/07 10:14:00
 */
@Controller
public class SynExcelExportController {

	private static final Logger logger = LoggerFactory.getLogger(SynExcelExportController.class);

	public static final String XLSX = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
	public static final String XLS = "application/vnd.ms-excel";
	public static final int BUFFER_SIZE = 2048;

	@Resource
	private ExportLockAspect exportLockAspect;


	/**
	 * 导出查询逻辑,查询是否这个表可以被导出(AJAX) 说明:当前方法为AJAX方法 理论上和/export/synExportError.shtml方法不混用 当前方法 需要页面上进行ajax控制 为最优解决方案
	 *
	 * @param id lockerId 通过这个ID查询出来这个表
	 * @return the map
	 * @throws java.io.IOException the iO exception
	 * @author zhangbin
	 * @date 2015 /05/14 09:21:58
	 */
	@RequestMapping(value = "${adminPath}/export/checkLocker.shtml")
	@ResponseBody
	public Map checkLocker(String id) throws IOException {
		Map<String, Object> result = new HashMap<String, Object>();
		if (StringUtils.isNotBlank(id)) {
			result.put("state", exportLockAspect.canBeenLocked(id));
			result.put("value",exportLockAspect.getStateObject(id));
		}
		return result;
	}

	/**
	 * 发生错误情况下,将会转发到当前页面 当前页面完成如下功能 1 创建错误信息 根据message 2 查询上个提交的页面的位置 3 js控制返回上个页面 说明:当前方法为标准sevlet方法
	 * 理论上和/export/checkLocker.shtml方法不混用 当前方法 在控制器被注释的情况下 默认被调用 为了方式忘记ajax方式或者前端验证被绕过
	 *
	 * @param request  the request
	 * @param response the response
	 * @param message  the message
	 * @return the string
	 * @throws java.io.IOException the iO exception
	 * @author zhangbin
	 * @date 2015 /05/14 14:59:20
	 */
	@RequestMapping(value = "${adminPath}/export/synExportError.shtml")
	public void exportError(HttpServletRequest request, HttpServletResponse response, String message)
			throws IOException {
		String referer = request.getHeader("Referer"); //获取请求的源地址
		request.setAttribute("referer", referer); //将源地址作为参数传递给错误处理页面
		if (StringUtils.isBlank(message)) {
			message = "导出报表发生同步错误!请勿重复导出!";
		}
		renderErrorResponse(response, referer, message);
	}

	@RequestMapping(value = "${adminPath}/export/synExcel.shtml")
	public void synExport(HttpServletRequest request, HttpServletResponse response) throws IOException {
		boolean state = (Boolean) request.getAttribute("state");
		logger.info("报表导出 下载转换 start!");
		if (state) {
			String fileName = (String) request.getAttribute("fileName");
			String downLoadName = "attachment;filename=" + fileName + ".xlsx";
			FileInputStream input = null;
			ServletOutputStream output = null;
			File excelFile = excelFile = (File) request.getAttribute("file");
			try {
				response.reset();
				response.setContentType(XLSX);
				response.setHeader("Content-Disposition", downLoadName);
				input = new FileInputStream(excelFile);
				output = response.getOutputStream();
				/*if (1 == 1) {
					throw new IOException("测试发生IO异常");
				}*/
				copyStream(input, output, true);
			} catch (IOException e) {
				logger.warn("导出表发生异常,从文件流写入错误", e);
				exportError(request, response, "下载文件失败!");
			} catch (RuntimeException e) {
				//当前异常发生应当是连接被释放 因此不做处理
				logger.warn("连接冲刷,被释放,无法写入流对象(点击后未导出完成,就将当前连接关闭)");
				return;
			} finally {
				releseResource(input, output, excelFile);
			}
		} else {
			String message = (String) request.getAttribute("message");
			exportError(request, response, message);
		}
		logger.info("报表导出 下载转换 end!");
	}

	/**
	 * 对于错误信息,创建一个页面
	 *
	 * @param response the response
	 * @author zhangbin
	 * @date 2015 /05/06 17:01:15
	 */
	private void renderErrorResponse(HttpServletResponse response, String referer, String message) throws IOException {
		response.reset();
		String html = "<html>"
					  + "<head>"
					  + "<meta charset=\"utf-8\">"
					  + "</head>"
					  + "<body>"
					  + "<div style=\"width: 100%;height: 30px;text-align: center;\">"
					  + message
					  + "<br>"
					  + "<a href=\"javascript:void(0);\" onclick=\"export_callback\">点击返回</a><span id=\"span_times\" style=\"color: red;\">5秒</span>后自动返回"
					  + "<script type=\"text/javascript\">"
					  + "        var int_time = 5;"
					  + "        var function_time;"
					  + "        function_time = setInterval(loaderTime,1000);"
					  + "        function loaderTime(){"
					  + "            document.getElementById(\"span_times\").innerHTML = int_time  + \"秒\";"
					  + "            int_time --;"
					  + "            if(int_time<=0){"
					  + "                clearInterval(function_time);"
					  + "                export_callback();"
					  + "            }"
					  + "        }"
					  + "        function export_callback(){"
					  + "            document.getElementById(\"_export_error_form\").submit();"
					  + "        }"
					  + ""
					  + "</script>"
					  + "<form id=\"_export_error_form\" action=\"" + referer + "\">"
					  + "<input type=\"hidden\" name=\"referer\" value=\"\"/>"
					  + "</form>"
					  + "</div>"
					  + "</body>"
					  + "</html>";
		response.setCharacterEncoding("UTF-8");
		PrintWriter out = response.getWriter();
		out.println(html);
		out.flush();
		out.close();
	}

	/**
	 * 释放固有资源 任何情况下 这些资源都应被释放
	 *
	 * @param input     文件输出流
	 * @param output    servlet输出流
	 * @param excelFile 文件
	 * @author zhangbin
	 * @date 2015 /05/06 16:53:48
	 */
	private void releseResource(FileInputStream input, ServletOutputStream output, File excelFile) {
		try {
			if (output != null) {
				output.close();
			}
		} catch (IOException e) {
			logger.warn("释放文件输出流发生异常", e);
		}
		try {
			if (input != null) {
				input.close();
			}
		} catch (IOException e) {
			logger.warn("释放sevlet输入流发生异常", e);
		}
		try {
			if (excelFile != null && excelFile.exists()) {
				logger.info("init delete file path={}", excelFile.getAbsolutePath());
				FileUtils.forceDelete(excelFile); //此处将文件进行强行删除
			}
		} catch (IOException e) {
			logger.warn("释放文件(删除)发生异常", e);
		}
	}

	/**
	 * 将文件流写入到servlet流中
	 *
	 * @param source the source
	 * @param dest   the dest
	 * @param flush  the flush
	 * @return the long
	 * @throws java.io.IOException the iO exception
	 * @author zhangbin
	 * @date 2015 /05/07 18:08:33
	 */
	private final long copyStream(InputStream source, OutputStream dest, boolean flush) throws RuntimeException {
		int bytes;
		long total = 0;
		try {
			total = 1;
			byte[] buffer = new byte[BUFFER_SIZE];
			while ((bytes = source.read(buffer)) != -1) {
				if (bytes == 0) {
					bytes = source.read();
					if (bytes < 0) {
						break;
					}
					dest.write(bytes);
					if (flush) {
						dest.flush();
					}
					total += bytes;
				}
				dest.write(buffer, 0, bytes);
				if (flush) {
					dest.flush();
				}
				total += bytes;
			}
		} catch (IOException e) {
			logger.warn("export file Exception", e);
			//异常发生原因,通常为写入流被断开
			throw new RuntimeException("文件导出发生异常");
		}
		return total;
	}


}
